/**
*  
* @Function 批量核查服务 
* @ProjectName credit-data-platform 
* @PackageName com.py.credit.service.impl    
* @FileName BatchReportServiceImpl.java    
* @Date 2018年10月19日上午10:05:34
* @Author Min DongXv
* @Copyright (c) 2018, 金电联行 All Rights Reserved.   
*   
*/
package com.py.credit.service.impl;

import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.net.URLEncoder;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;

import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang3.StringUtils;
import org.apache.poi.hssf.usermodel.HSSFCell;
import org.apache.poi.hssf.usermodel.HSSFDateUtil;
import org.apache.poi.hssf.usermodel.HSSFRow;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.web.multipart.MultipartFile;

import com.py.credit.common.rest.RestServiceResult;
import com.py.credit.dao.BatchReportDao;
import com.py.credit.entity.BatchReportDetailEntity;
import com.py.credit.entity.BatchReportEntity;
import com.py.credit.entity.CreateReportResult;
import com.py.credit.entity.CreateReportTask;
import com.py.credit.entity.SysUser;
import com.py.credit.service.BatchReportDetailService;
import com.py.credit.service.BatchReportService;
import com.py.credit.service.CreditReportService;
import com.py.credit.service.SSearchService;
import com.py.credit.service.ScontentService;
import com.py.credit.service.excel.read.service.ExcelReadService;
import com.py.credit.utils.DateUtil;
import com.py.credit.utils.MD5Util;
import com.py.credit.utils.SQLBuilder;
import com.py.credit.utils.ZipUtil;
import com.py.framework.core.common.Page;
import com.py.framework.core.redis.bis.JedisBisManage;
import com.py.framework.core.utils.ThreadExecutor;

import redis.clients.jedis.Jedis;

/**
 * 
 * @Function 批量核查服务
 * @ClassName BatchReportServiceImpl
 * @Date 2018年10月19日 上午10:05:34
 * @Author Min DongXv
 * 
 */
@Service
public class BatchReportServiceImpl implements BatchReportService {

	private static final int NUM_1800 = 1800;

	private static final int NUM_1000 = 1000;

	private static final int ZERO = 0;

	private static final String REPEATNUM = "repeatNum";

	private static final String ALLNUM = "allNum";

	private static final String BATCHID = "batchId";

	@Autowired
	private BatchReportDao batchReportDao;

	@Autowired
	private ExcelReadService excelReadService;

	@Autowired
	private BatchReportDetailService batchReportDetailService;

	@Autowired
	private CreditReportService creditReportService;

	@Autowired
	private ScontentService sContentService;

	@Autowired
	private SSearchService sSearchService;

	@Autowired
	private BgdzServiceImpl bgdzService;

	/**
	 *
	 * @Function 分页查询批量核查
	 * @FileName BatchReportServiceImpl.java
	 * @MethodName getBatchReport
	 * @param columns
	 * @param additionalColumns
	 * @param conditions
	 * @param page
	 * @return
	 * @Date 2018年10月19日 上午10:06:10
	 * @Author Min DongXv
	 *
	 */
	@Override
	public List<Map<String, Object>> getBatchReport(List<String> columns, String[] additionalColumns, String conditions,
			Page page,Long deptId,Long userId) {
		List<String> condition = SQLBuilder.createConditionSql(conditions);
		return batchReportDao.getBatchReportByConditionForPage(columns, additionalColumns, condition, page,deptId,userId);
	}

	/**
	 *
	 * @Function 更新批量核查
	 * @FileName BatchReportServiceImpl.java
	 * @MethodName editBatchReport
	 * @param map
	 * @Date 2018年10月19日 上午10:06:10
	 * @Author Min DongXv
	 *
	 */
	@Override
	public void updateBatchReport(Map<String, Object> map) {
		batchReportDao.updateBatchReport(map);
	}

	/**
	 * 
	 *
	 * @Function 上传批量核查
	 * @FileName BatchReportServiceImpl.java
	 * @MethodName uploadBatchReport
	 * @param file
	 *            文件
	 * @param userInfo
	 *            上传用户信息
	 * @param absolutepath
	 *            文件存放路径
	 * @return
	 * @Date 2018年10月19日 上午11:17:26
	 * @Author Min DongXv
	 *
	 */
	@Override
	public RestServiceResult<Object> uploadBatchReport(MultipartFile file, SysUser userInfo, String absolutepath,
			String batchId) {
		RestServiceResult<Object> result = new RestServiceResult<>();
		// 文件存储路径
		String filePath = createPath(absolutepath, batchId);
		// 文件的后缀
		String fileType = null;
		if (null != file) {
			String name = file.getOriginalFilename();
			// 获取文件后缀
			if (name.contains(".")) {
				int l = name.lastIndexOf('.');
				fileType = name.substring(l, name.length());
			}
			if (!".xls".equalsIgnoreCase(fileType) && !".xlsx".equalsIgnoreCase(fileType)) {
				result.setCode(RestServiceResult.CODE_UNKNOW);
				result.setMsg("上传文件格式不支持，请上传excel文件！");
				return result;
			}
		} else {
			result.setCode(RestServiceResult.CODE_UNKNOW);
			result.setMsg("上传的文件不得为空");
			return result;
		}
		File uploadFile = null;
		File dirPath = new File(filePath);
		uploadFile = new File(filePath + batchId + fileType);
		if (!dirPath.exists()) {
			dirPath.mkdirs();
		}
		try {
			file.transferTo(uploadFile);
			BatchReportEntity entity = new BatchReportEntity(batchId, userInfo.getOfficeId(), userInfo.getId(),
					batchId + fileType, filePath);
			batchReportDao.addBatchReport(entity);
		} catch (IllegalStateException | IOException e) {
			result.setCode(RestServiceResult.CODE_UNKNOW);
			result.setMsg("文件存储异常，请重新上传！");
			return result;
		}
		result.setData(filePath + batchId + fileType);
		return result;
	}

	/**
	 * 
	 * @Function 解析批量核查报告
	 * @FileName BatchReportServiceImpl.java
	 * @MethodName dealFile
	 * @param batchId
	 *            返回结果集
	 * @param filePath
	 * @return
	 * @Date 2018年10月22日 下午5:06:49
	 * @Author Min DongXv
	 *
	 */
	@Override
	public RestServiceResult<Object> dealFile(String filePath, String batchId) {
		RestServiceResult<Object> result = new RestServiceResult<>();
		try {
			// 读取excel
			Sheet st = excelReadService.readExcelFileForSheet(filePath);
			// 校验表头
			Row row = st.getRow(0);
			if (null != row && null != row.getCell(0) && null != row.getCell(1)) {
				if (!"企业名称".equals(row.getCell(0).toString().trim())
						&& !"统一社会信用代码".equals(row.getCell(1).toString().trim())&& !"用途".equals(row.getCell(2).toString().trim())) {
					result.setCode(RestServiceResult.CODE_UNKNOW);
					result.setMsg("模板表头已被更改,请用规定的模板上传数据。");
				}
			} else {
				result.setCode(RestServiceResult.CODE_UNKNOW);
				result.setMsg("未检测到模板表头,请用规定的模板上传数据。");
			}
			// 表头有问题直接返回异常提醒
			if (RestServiceResult.CODE_UNKNOW == result.getCode()) {
				Map<String, Object> map = new HashMap<>(2);
				map.put(BATCHID, batchId);
				map.put("batchStatus", 4);
				batchReportDao.updateBatchReport(map);
				return result;
			} else {
				return dealSheet(st, batchId);
			}
		} catch (Exception e) {
			result.setCode(RestServiceResult.CODE_UNKNOW);
			result.setMsg("文件内容读取异常，请重新上传！");
			return result;
		}
	}

	/**
	 * 
	 * @Function 解析数据
	 * @FileName BatchReportServiceImpl.java
	 * @MethodName dealSheet
	 * @param st
	 *            sheet对象
	 * @return
	 * @Author Min DongXv
	 *
	 */
	private RestServiceResult<Object> dealSheet(Sheet st, String batchId) throws Exception {
		RestServiceResult<Object> result = new RestServiceResult<>();
		boolean reStart = true;
		int i = 0;
		int start = 0;
		int end = 0;
		// 数据总条数
		int allRowNum = st.getLastRowNum();
		Map<String, Integer> repeatMap = new HashMap<>(1);
		repeatMap.put(REPEATNUM, 0);
		repeatMap.put(ALLNUM, 0);
		Set<String> allData = new HashSet<>();
		while (reStart) {
			start = i * NUM_1000 + 1;
			end = i * NUM_1000 + NUM_1000;
			if (end > allRowNum) {
				end = allRowNum;
				reStart = false;
			}
			List<BatchReportDetailEntity> data = readExcelData(start, end, st, allData, batchId, repeatMap);
			boolean dataEmpty = CollectionUtils.isEmpty(data);
			if (dataEmpty && ZERO == i) {
				result.setCode(RestServiceResult.CODE_UNKNOW);
				result.setMsg("上传文件数据不得为空");
				Map<String, Object> map = new HashMap<>(2);
				map.put(BATCHID, batchId);
				map.put("batchStatus", 4);
				batchReportDao.updateBatchReport(map);
				return result;
			} else if (dataEmpty) {
				reStart = false;
			} else {
				// 批量插入报告详情信息
				batchReportDetailService.batchAddDetails(data);
			}
			i++;
		}
		// 更新关联状态
		batchReportDetailService.updateDetailInfo(batchId);
		// 获取关联上的企业信息
		Integer num = batchReportDetailService.getWaitCreateDetailCount(batchId);
		Map<String, Object> map = new HashMap<>(4);
		map.put(BATCHID, batchId);
		map.put("companyAssnum", num);
		map.put("companyNum", repeatMap.get(ALLNUM));
		map.put(REPEATNUM, repeatMap.get(REPEATNUM));
		// 更新批次中关联企业数量
		batchReportDao.updateBatchReport(map);
		return result;
	}

	/**
	 * 
	 * @Function 提取数据
	 * @FileName BatchReportServiceImpl.java
	 * @MethodName readExcelData
	 * @param start
	 *            开始为止
	 * @param end
	 *            结束为止
	 * @param st
	 *            sheet对象
	 * @return
	 * @Author Min DongXv
	 *
	 */
	private List<BatchReportDetailEntity> readExcelData(int start, int end, Sheet st, Set<String> allData,
			String batchId, Map<String, Integer> repeatMap) {
		List<BatchReportDetailEntity> data = new ArrayList<>();
		Row row = null;
		for (int rowIndex = start; rowIndex <= end; rowIndex++) {
			String name = null;
			String code = null;
			String yt = null;
			row = st.getRow(rowIndex);
			if (row == null) {
				continue;
			}
			Cell nameCell = row.getCell(0, Row.RETURN_BLANK_AS_NULL);
			Cell codeCell = row.getCell(1, Row.RETURN_BLANK_AS_NULL);
			Cell ytCell = row.getCell(2, Row.RETURN_BLANK_AS_NULL);
			if (null != nameCell) {
				name = getCellData(nameCell).trim();
			}
			if (null != codeCell) {
				code = getCellData(codeCell).trim();
			}
			if (null != ytCell) {
				yt = getCellData(ytCell).trim();
			}
			if (StringUtils.isNotBlank(name) || StringUtils.isNotBlank(code)) {
				BatchReportDetailEntity entity = new BatchReportDetailEntity();
				// excel去重
				String uniqueCode = MD5Util.getInstance().md5s((name + "|" + code).getBytes());
				repeatMap.put(ALLNUM, repeatMap.get(ALLNUM) + 1);
				if (!allData.contains(uniqueCode)) {
					allData.add(uniqueCode);
					entity.setA000001(replaceSpecialChar(lengthChange(name, 256)));
					entity.setA000002(replaceSpecialChar(lengthChange(code, 64)));
					entity.setBatchId(batchId);
					entity.setYt(replaceSpecialChar(lengthChange(yt, 64)));
					entity.setReportName(name + ".pdf");
					data.add(entity);
				} else {
					repeatMap.put(REPEATNUM, repeatMap.get(REPEATNUM) + 1);
				}
			}
		}
		return data;
	}

	/**
	 * 
	 * @Function 获取单元格数据
	 * @FileName BatchReportServiceImpl.java
	 * @MethodName getCellData
	 * @param cell
	 *            单元格
	 * @return
	 * @Author Min DongXv
	 *
	 */
	private String getCellData(Cell cell) {
		String value = "";
		switch (cell.getCellType()) {
		case HSSFCell.CELL_TYPE_STRING:
			value = cell.getStringCellValue().trim();
			break;
		case HSSFCell.CELL_TYPE_NUMERIC:
			if (HSSFDateUtil.isCellDateFormatted(cell)) {
				Date date = cell.getDateCellValue();
				if (date != null) {
					value = DateUtil.getInstance().dateToStr(date, DateUtil.YYYY__MM__DD);
				} else {
					value = "";
				}
			} else {
				value = new DecimalFormat("#.####").format(cell.getNumericCellValue());
			}
			break;
		case HSSFCell.CELL_TYPE_FORMULA:
			break;
		case HSSFCell.CELL_TYPE_BLANK:
			break;
		case HSSFCell.CELL_TYPE_ERROR:
			break;
		case HSSFCell.CELL_TYPE_BOOLEAN:
			value = (true == cell.getBooleanCellValue() ? "Y" : "N");
			break;
		default:
			value = "";
		}
		return value;
	}

	/**
	 * 
	 * @Function 特殊字符替换
	 * @FileName BatchReportServiceImpl.java
	 * @MethodName replaceSpecialChar
	 * @param value
	 *            待替换的字符串
	 * @return
	 * @Author Min DongXv
	 *
	 */
	private String replaceSpecialChar(String value) {
		// 特殊字符转化，'会引起sql拼接出现问题
		if (StringUtils.isNotBlank(value)) {
			value = value.replace("\\", "\\\\");
			if (value.contains("'")) {
				value = value.replace("'", "\\'");
			}
			if (value.contains("\"")) {
				value = value.replace("\"", "\\\"");
			}
		}
		return value;
	}

	/**
	 * 
	 * @Function 长度截取
	 * @FileName BatchReportServiceImpl.java
	 * @MethodName lengthChange
	 * @param value
	 *            待替换的字符串
	 * @param size
	 *            长度
	 * @return
	 * @Author Min DongXv
	 *
	 */
	private String lengthChange(String value, int size) {
		if (StringUtils.isNotBlank(value) && value.length() > size) {
			value = value.substring(0, size);
		}
		return value;
	}

	/**
	 * 
	 * @Function 批量核查模板下载
	 * @FileName BatchReportServiceImpl.java
	 * @MethodName templateDownload
	 * @param response
	 * @return
	 * @Date 2018年10月22日 上午11:50:33
	 * @Author Min DongXv
	 *
	 */
	@Override
	public RestServiceResult<Object> templateDownload(HttpServletResponse response) {
		RestServiceResult<Object> result = new RestServiceResult<>();
		response.setContentType("application/vnd.ms-excel");
		// 产生工作簿对象
		try (HSSFWorkbook workbook = new HSSFWorkbook(); OutputStream fOut = response.getOutputStream()) {
			// 进行转码，使其支持中文文件名
			String fileNanme = URLEncoder.encode("批量核查", "UTF-8");
			response.setHeader("content-disposition", "attachment;filename=" + fileNanme + ".xls");
			// 产生工作表对象
			HSSFSheet sheet = workbook.createSheet();
			workbook.setSheetName(0, "批量核查");
			HSSFRow row = null;
			HSSFCell cell = null;
			row = sheet.createRow(0);
			cell = row.createCell(0);
			cell.setCellType(HSSFCell.CELL_TYPE_STRING);
			cell.setCellValue("企业名称");
			cell = row.createCell(1);
			cell.setCellType(HSSFCell.CELL_TYPE_STRING);
			cell.setCellValue("统一社会信用代码");
			cell = row.createCell(2);
			cell.setCellType(HSSFCell.CELL_TYPE_STRING);
			cell.setCellValue("用途");
			workbook.write(fOut);
			fOut.flush();
			return result;
		} catch (IOException e) {
			result.setCode(RestServiceResult.CODE_UNKNOW);
			result.setMsg("模板下载异常！");
			return result;
		}
	}

	/**
	 * 
	 * @Function 生成报告定时任务
	 * @FileName BatchReportServiceImpl.java
	 * @MethodName createReport
	 * @return
	 * @throws InterruptedException
	 * @throws Exception
	 * @Date 2018年10月23日 上午11:03:58
	 * @Author Min DongXv
	 *
	 */
	@Override
	public synchronized RestServiceResult<Object> createReport(String absolutepath, String pdfJsPath, String googlePath,
			String pdfPagePath) throws InterruptedException {
		RestServiceResult<Object> result = new RestServiceResult<>();
		// 获取生成中报告的数量，如果有，不执行生成操作(一小时之前还是生成中的详情不计入数量，可以进行重新生成)
		int creatingNum = batchReportDetailService.getCreatingNum();
		if (0 >= creatingNum) {
			// 把报告待生成状态变成生成中
			batchReportDetailService.batchUpdateStatus();
			// 获取生成中状态的报告
			List<BatchReportDetailEntity> data = batchReportDetailService.getWaitCreateDetail(null);
			if (!CollectionUtils.isEmpty(data)) {
				String path = absolutepath + File.separator + "batchReport" + File.separator;
				// 5个线程一起执行
				ExecutorService newFixedThreadPool = ThreadExecutor.newFixedThreadPool(5);
				// 执行返回状态
				List<Future<CreateReportResult>> taskResult = new ArrayList<>();
				Set<String> batchIdList = new HashSet<>();
				for (BatchReportDetailEntity e : data) {
					if (null != e) {
						batchIdList.add(e.getBatchId());
						String filePath = path + e.getBatchId();
						CreateReportTask task = new CreateReportTask(filePath, pdfJsPath, googlePath, pdfPagePath, absolutepath,
								"100000", e, batchReportDetailService, creditReportService, sContentService, sSearchService, bgdzService);
						// 报告生成
						Future<CreateReportResult> future = newFixedThreadPool.submit(task);
						taskResult.add(future);
					}
				}
				// 更新批次状态
				if (!CollectionUtils.isEmpty(batchIdList)) {
					newFixedThreadPool.shutdown();
					Object o = new Object();
					synchronized (o) {
						while (!newFixedThreadPool.isTerminated()) {
							o.wait(1000);
						}
						batchReportDao.updateBatchStatus(batchIdList);
						List<String> finishedBatchs = batchReportDao.getFinishBatchStatus(batchIdList);
						if (!CollectionUtils.isEmpty(finishedBatchs)) {
							for (String s : finishedBatchs) {
								String fullPath = createPath(absolutepath, s);
								ZipUtil.zipByAnt(fullPath, "批量核查.zip", fullPath, "*.pdf", null);
							}
						}
					}
				}
				ThreadExecutor.close(newFixedThreadPool);
			}
		}
		return result;
	}

	/**
	 * 
	 * @Function 批量核查打包下载
	 * @FileName BatchReportServiceImpl.java
	 * @MethodName zipDownload
	 * @param zipPath
	 *            文件存放路径
	 * @param batchId
	 *            批次ID
	 * @param response
	 * @param zipName
	 *            压缩包名称
	 * @return
	 * @Date 2018年10月23日 下午5:10:20
	 * @Author Min DongXv
	 *
	 */
	@Override
	public RestServiceResult<Object> zipDownload(String batchId, String zipPath, HttpServletResponse response,
			String zipName) {
		RestServiceResult<Object> result = ZipUtil.zipDownload(zipName, response, zipPath);
		if (RestServiceResult.CODE_SUCCESS == result.getCode()) {
			Jedis jedis = JedisBisManage.getJedis();
			jedis.del(batchId);
		}
		return result;
	}

	/**
	 * 
	 * @Function 是否可以生成压缩包
	 * @FileName BatchReportServiceImpl.java
	 * @MethodName zipCanDownload
	 * @param absolutepath
	 *            文件存放地址
	 * @param batchId
	 *            批次信息
	 * @return
	 * @Date 2018年10月25日 下午2:24:31
	 * @Author Min DongXv
	 *
	 */
	@Override
	public RestServiceResult<String> zipCanDownload(String absolutepath, String batchId, String zipName) {
		RestServiceResult<String> result = new RestServiceResult<>();

		Jedis jedis = JedisBisManage.getJedis();
		if (jedis.exists(batchId)) {
			result.setCode(RestServiceResult.CODE_UNKNOW);
			result.setMsg("文件正在打包，请稍后下载");
			return result;
		} else {
			jedis.set(batchId, batchId);
			jedis.expire(batchId, NUM_1800);
		}
		List<String> fileNames = batchReportDetailService.getReportName(batchId);
		if (CollectionUtils.isEmpty(fileNames)) {
			result.setCode(RestServiceResult.CODE_UNKNOW);
			result.setMsg("没有已生成的报告");
		} else {
			String path = createPath(absolutepath, batchId);
			// 只打包pdf
			RestServiceResult<String> zipResult = ZipUtil.zipByAnt(path, zipName, path, "*.pdf", null);
			if (RestServiceResult.CODE_UNKNOW == zipResult.getCode()) {
				result.setCode(RestServiceResult.CODE_UNKNOW);
				result.setMsg(zipResult.getMsg());
				return result;
			}
			result.setData(zipResult.getData());
		}
		return result;
	}

	private String createPath(String absolutepath, String batchId) {
		return absolutepath + File.separator + "batchReport" + File.separator + batchId + File.separator;
	}
}